/*
 * Copyright (C) 2008 Peter Kling
 * 
 * This file is part of 'A Kind of Billiard', a graphical billiard simulation.
 * 
 * 'A Kind of Billiard' is free software: you can redistribute it and/or modify it under the terms of the GNU General
 * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 * 
 * 'A Kind of Billiard' is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with 'A Kind of Billiard'. If not, see
 * <http://www.gnu.org/licenses/>.
 */

package de.akob.physics;

import de.akob.mathematics.MathVector;
import de.akob.mathematics.Mathematics;

/**
 * Class that collects physical constants and static methods of the physic engine. If not stated else, all units are SI
 * units.
 * 
 * @see "http://en.wikipedia.org/wiki/International_System_of_Units"
 */
public final class Physics {
	public static final double gravity_earth = 9.816;
	public static final double gravity_moon  = 1.622;
	
//	/**
//	 * Collision handling between a ball and a plane.
//	 * 
//	 * @return True if there was a collision, false otherwise.
//	 */
//	public static boolean handleCollision(MathPlane3D plane, PhysicalBall ball) {
//		return handleCollision(ball, plane);
//	}
	
//	/**
//	 * Collision handling between a ball and a plane. A collision between a ball and a plane is recognized if the ball
//	 * is penetrating the plane and if it is approaching the plane (that is its velocity vector points from the ball
//	 * center towards the plane). The ball will lose 25% of its energy in plane-normal direction.
//	 * 
//	 * @return True if there was a collision, false otherwise.
//	 */
//	public static boolean handleCollision(PhysicalBall ball, MathPlane3D plane) {
//		double distance_oriented = Mathematics.distance_oriented(plane, ball.getPosition());
//		double distance = Math.abs(distance_oriented);
//		if (distance >= ball.getRadius())
//			return false;
//		
//		// check whether we are approaching the plane
//		double projection = Mathematics.mul(plane.getNormal(), ball.getVelocity());
//		if (distance_oriented > 0) { // ball is nearer to the origin than the plane
//			if (projection <= 0.)
//				return false;
//		} else { // plane is nearer to the origin than the ball
//			if (projection >= 0.)
//				return false;
//		}
//		
//		// update velocity vector
//		MathVector velocity = Mathematics.mirrorVector(plane, ball.getVelocity());
//		velocity.add(Mathematics.mul(plane.getNormal(), projection * 0.25));
//		ball.setVelocity(velocity);
//		
//		// make sure that ball is moved out of plane
//		if (distance_oriented > 0) // ball is nearer to the origin than the plane
//			Physics.moveObject(ball, Mathematics.mul(plane.getNormal(), -(ball.getRadius() - distance)));
//		else
//			// plane is nearer to the origin than the ball
//			Physics.moveObject(ball, Mathematics.mul(plane.getNormal(), ball.getRadius() - distance));
//		
//		return true;
//	}
	
//	/**
//	 * Collision handling between two balls. A collision between two balls is only recognized if they are penetrating
//	 * each other and if at least one of them is approaching the other.
//	 * 
//	 * @return True if there was a collision, false otherwise.
//	 */
//	public static boolean handleCollision(PhysicalBall ball1, PhysicalBall ball2) {
//		if (Mathematics.distance(ball1.getPosition(), ball2.getPosition()) >= ball1.getRadius() + ball2.getRadius())
//			return false;
//		
//		// get velocities and direction of impact
//		MathVector velocity1 = ball1.getVelocity();
//		MathVector velocity2 = ball2.getVelocity();
//		MathVector directionOfImpact = Mathematics.sub(ball2.getPosition(), ball1.getPosition());
//		directionOfImpact.normalize();
//		
//		// if not at least one of the balls is approaching the other we cancel the collision handling
//		if (Mathematics.mul(velocity1, directionOfImpact) / velocity1.norm() <= 0
//		        && Mathematics.mul(velocity2, directionOfImpact) / velocity2.norm() >= 0)
//			return false;
//		
//		// compute the projection of the velocities in impact direction
//		double impactFraction1 = Mathematics.mul(velocity1, directionOfImpact);
//		double impactFraction2 = Mathematics.mul(velocity2, directionOfImpact);
//		
//		// compute new velocities
//		double massSum = ball1.mass + ball2.mass;
//		double massDiff = ball1.mass - ball2.mass;
//		double transferredFraction1 = (massDiff * impactFraction1 + 2 * ball2.mass * impactFraction2) / massSum;
//		double transferredFraction2 = (-massDiff * impactFraction2 + 2 * ball1.mass * impactFraction1) / massSum;
//		velocity1.sub(Mathematics.mul(directionOfImpact, impactFraction1));
//		velocity1.add(Mathematics.mul(directionOfImpact, transferredFraction1));
//		velocity2.sub(Mathematics.mul(directionOfImpact, impactFraction2));
//		velocity2.add(Mathematics.mul(directionOfImpact, transferredFraction2));
//		ball1.setVelocity(velocity1);
//		ball2.setVelocity(velocity2);
//		
//		return true;
//	}
	
//	/**
//	 * Collision handling between a ball and a simple obstacle. Collision handling is nearly identical to collision
//	 * handling regarding balls and planes. The only difference is, that collision handling will only be performed if
//	 * the ball is not currently within some of the 'hole regions' of the obstacle.
//	 * 
//	 * @return True if there was a collision, false otherwise.
//	 */
//	public static boolean handleCollision(PhysicalBall ball, SimpleObstacle obstacle) {
//		for (SimpleObstacle.Cuboid hole : obstacle.getHoles()) {
//			if (hole.contains(ball.getPosition()))
//				return false;
//		}
//		return handleCollision(ball, obstacle.getPlane());
//	}
	
//	/**
//	 * Collision handling between a ball and a plane.
//	 * 
//	 * @return True if there was a collision, false otherwise.
//	 */
//	public static boolean handleCollision(SimpleObstacle obstacle, PhysicalBall ball) {
//		return handleCollision(ball, obstacle);
//	}
	
	/**
	 * Moves a ball using the given translation vector.
	 * 
	 * @throws IllegalArgumentException If the translation vector does not have dimension three.
	 */
	public static void moveObject(PhysicalObject object, MathVector translation) {
		if (translation.dim != 3)
			throw new IllegalArgumentException("Given translation vector does not have dimension three.");
		object.setPosition(Mathematics.add(object.getPosition(), translation));
	}
}
